iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
Modern Web

從 Next.js 開始的 Functional Programming系列 第 22

D22 - 實作異步流程 (八)

  • 分享至 

  • xImage
  •  

今天開始準備做後端的 API 囉~~~

https://ithelp.ithome.com.tw/upload/images/20231007/20158615hvkOl6BhQa.png

我們同樣秉持測試驅動開發的精神,在還沒開始之前就先設計、撰寫測試,然後才是開發與重構。

程式碼請參考 D22/consumer-driven-contract

消費者驅動合約

消費者驅動合約 ( consumer driven contract ),依照我的理解一言以蔽之就是

根據 API 被使用 的方式來定義文件並進行測試。

現在已經有工具例如 Pact.js 可以幫助大家實踐,有興趣的話可以參考看看。

接下來我們會參考它背後的理念來設計測試,因此我們要知道 API 如何被使用,而這其實就跟昨天的主題有關

前端單元測試

// src\service\contracts\get-user.spec.ts
import ...

describe('GET /api/v1/users/:username', () => {
  const baseUrl = 'http://localhost'
  const server = setupServer()
  beforeAll(() => server.listen())
  afterEach(() => server.resetHandlers())
  afterAll(() => server.close())
  describe('when exists user richard_00 to richard_99', () => {
    beforeEach(() => {
      server.use(
        getUserHandler(baseUrl)('exists user richard_00 to richard_99')
      )
    })
    it('should get a user when username is richard_01', async () => {
      //arrange
      const username = 'richard_01'
      //act
      const result = await pipe(
        getUser(username),
        Effect.merge,
        Effect.runPromise
      )
      //assert
      const data = S.parseSync(User.schema)(result)
      expect(data.name).toBe(username)
    })

    it('should get not found error when username is richard_x', async () => {
      //arrange
      const name = 'richard_x'
      //act
      const result = await pipe(getUser(name), Effect.merge, Effect.runPromise)
      //assert
      expect(S.is(NotFoundError.struct)(result)).toBe(true)
    })
  })

  it('should get unexpected axios error when internal error', async () => {
    //arrange
    server.use(getUserHandler(baseUrl)('database connection is broken'))
    const name = 'richard_01'
    //act
    const result = await pipe(getUser(name), Effect.merge, Effect.runPromise)
    //assert
    expect(S.is(UnexpectedAxiosError.struct)(result)).toBe(true)
  })
})

前端的單元測試會詳細的告訴你,它期待 API 有哪些行為,撇開可以避免的格式錯誤不談,以上測試內容可以轉換成以下合約

在存在使用者 richard_00richard_99 的狀態下

  1. 前端送出 GET /api/v1/users/richard_01 請求,會收到狀態碼 200 加上格式正確的使用者物件
  2. 前端送出 GET /api/v1/users/richard_x 請求,會收到狀態碼 404

在後端發生錯誤的情況下

  1. 前端送出 GET /api/v1/users/richard_01 請求,會收到狀態碼 500

上面的場景是最簡單的了,畢竟這就是單純的 GET 請求,沒有其他互動,所以相較於傳統基於 API 的測試沒有太大差異。

我們想像一個複雜點的場景

前端送出 POST /api/v1/courses/course123/users 請求,會收到 201 回應
POST /api/v1/courses/course123/users 請求成功的狀態下接著送出 GET /api/v1/courses 請求 ,會收到 200,而且回應的 課程物件列表裡面會有剛才新增的 使用者物件

在這樣的場景下,相對單純的 API 測試,消費者驅動合約就能帶給我們更多的信心。


上一篇
D21 - 實作異步流程 (七)
下一篇
D23 - 實作異步流程 (九)
系列文
從 Next.js 開始的 Functional Programming30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言